package winOrDie.GameEngine.Server;

import java.net.DatagramPacket;
import java.net.Socket;
import java.util.Date;

import winOrDie.Net.WoD_Net_Protocol_ASCII;
import winOrDie.Net.Proxy.Wod_Proxy_Server;
import winOrDie.Net.Server.WoD_Server_Client;
import winOrDie.Net.Server.WoD_Server_Client_List;
import winOrDie.Net.Server.WoD_Server_ControlChannel_Event;
import winOrDie.Net.Server.WoD_Server_ControlChannel_Listener;
import winOrDie.Net.Server.WoD_Server_RT;
import winOrDie.Net.Server.WoD_Server_RT_Event;
import winOrDie.Net.Server.WoD_Server_RT_Listener;
import winOrDie.Net.Server.WoD_TCP_Socket_Server;
import winOrDie.Net.Server.WoD_TCP_Socket_Server_Event;
import winOrDie.Net.Server.WoD_TCP_Socket_Server_Listener;


public class WoD_Server_Test 
implements WoD_TCP_Socket_Server_Listener, WoD_Server_ControlChannel_Listener, WoD_Server_RT_Listener
{
	
	private int port_UDP_RT = -1;
	private int port_TCP_CONTROL = -1;
	
	
	private WoD_Server_Client_List clientList = new WoD_Server_Client_List();
	private WoD_TCP_Socket_Server woD_TCP_Socket_Server = null;
	private WoD_Server_RT woDServerRT = null;
	
	private int id_counter = 0;
	
	private boolean proxyEnabled = false;
	private String proxy_IP = null;
	private int proxy_port = -1;
	
	
	public int getPort_UDP_RT() {
		return port_UDP_RT;
	}


	public void setPort_UDP_RT(int portUDPRT) {
		port_UDP_RT = portUDPRT;
	}


	public int getPort_TCP_CONTROL() {
		return port_TCP_CONTROL;
	}


	public void setPort_TCP_CONTROL(int portTCPCONTROL) {
		port_TCP_CONTROL = portTCPCONTROL;
	}

	
	

	public boolean isProxyEnabled() {
		return proxyEnabled;
	}


	public void setProxyEnabled(boolean proxyEnabled) {
		this.proxyEnabled = proxyEnabled;
	}


	public String getProxy_IP() {
		return proxy_IP;
	}


	public void setProxy_IP(String proxyIP) {
		proxy_IP = proxyIP;
	}


	public int getProxy_port() {
		return proxy_port;
	}


	public void setProxy_port(int proxyPort) {
		proxy_port = proxyPort;
	}

	
	WoD_Server_Client Proxy_client = null;
	

	public WoD_Server_Test() {
		
	}
	
	
	/**
	 * Notification of TCP Socket Server events
	 */
	public void notify_Event_WoD_TCP_Socket_Server(WoD_TCP_Socket_Server_Event event) {
		switch (event.getEvent_Type()) {
		
		case WoD_TCP_Socket_Server_Event.SERVER_STARTING:
			System.out.println("Iniciando el servidor.");
			break;
		
		case WoD_TCP_Socket_Server_Event.SERVER_INITIALICED:
			System.out.println("Servidor escuchando.");
			break;
		
		case WoD_TCP_Socket_Server_Event.NEW_CLIENT:
			
			/*
			 * Create new client
			 */
			Socket socket = (Socket) event.getData();
			WoD_Server_Client client = null; 
			try {
				client = new WoD_Server_Client(socket);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			/*
			 * Add this listener to the Control channel listeners
			 */
			client.getWoDServerControlChannel().add_WoD_Server_ControlChannel_Listener(this);
			
			/*
			 * Set client ID and hostAddress
			 * Add to the client list
			 */
			client.setCliend_Id(id_counter++);
			if (!isProxyEnabled())
			{
				client.setHostDirection(socket.getInetAddress().getHostAddress());
			}
			
			clientList.add_client(client);
			
			break;
			
		case WoD_TCP_Socket_Server_Event.SERVER_ERROR:
			System.out.println("[Server] Server error. " + (String)event.getData());
			break;

		default:
			break;
		}
		
	}
	
	
	
	private void send_BroadcastMessage(String message)
	{
		for (WoD_Server_Client client : clientList.getClients()) {
			try {
				if (proxyEnabled)
				{
					Wod_Proxy_Server.sendData_With_Proxy(client.getWoDServerControlChannel(), message);
					
				}
				else
				{
					client.getWoDServerControlChannel().sendData(message);
				}
				
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	
	
	/**
	 * Notification for Control channel events
	 */
	public void notify_Event_WoD_Client_ControlChannel(WoD_Server_ControlChannel_Event event) 
	{
		String message_received = null;
		String message_toSend = null;
		
		
		
		
		
		switch (event.getEvent_Type()) {
		case WoD_Server_ControlChannel_Event.JOIN_GAME:
			message_received = (String) event.getData();
			String message_parts[] = message_received.split(" ");
			
			int position = clientList.get_positionOfClient_By_UserName(message_parts[1]);
			if (position != -1)
			{
				/*
				 * If position is different than -1 the username already exists
				 */
				message_toSend = WoD_Net_Protocol_ASCII.GameMenu.Join_Game_KO + " "  + "User already exists.";
				try {
					event.getWoDServerClient().getWoDServerControlChannel().sendData(message_toSend);
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			else
			{
				/*
				 * Else set backup port and udp port.
				 * Also send "Join_Game_OK" message
				 */
				event.getWoDServerClient().setTcp_backup(Integer.valueOf(message_parts[2]));
				event.getWoDServerClient().setUdp_port(Integer.valueOf(message_parts[3]));
				
				
				message_toSend = WoD_Net_Protocol_ASCII.GameMenu.Join_Game_OK + " ";
				message_toSend += event.getWoDServerClient().getCliend_Id() + " ";
				message_toSend += port_UDP_RT;
				
				
				try {
					event.getWoDServerClient().getWoDServerControlChannel().sendData(message_toSend);
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			break;
		
		case WoD_Server_ControlChannel_Event.CHAT:
			
			message_received = (String) event.getData();
			message_toSend = message_received.substring(
					message_received.indexOf(" ") + 1, 
					message_received.length()
					);
			send_BroadcastMessage(WoD_Net_Protocol_ASCII.GameRoom.Chat + " " + event.getWoDServerClient().getCliend_Id() + " " +  message_toSend);
			
			break;

		case WoD_Server_ControlChannel_Event.LEAVE_GAME:
			message_toSend = WoD_Net_Protocol_ASCII.GameRoom.Leave_Game + " " + event.getWoDServerClient().getCliend_Id();
			send_BroadcastMessage(message_toSend);
			
			
			break;

		default:
			break;
		}
		
	}
	
	
	/**
	 * Notification for Real time Channel events
	 */
	public void notify_Event_WoD_Server_RTChannel(WoD_Server_RT_Event event) {
	
		switch (event.getEvent_Type()) {
		case WoD_Server_RT_Event.ERROR_DATAGRAM:
			System.out.println("[Server] Datagram error");
			break;
			
			
		case WoD_Server_RT_Event.ERROR_IO:
			System.out.println("[Server] Datagram IO error");
			break;
			
		case WoD_Server_RT_Event.ERROR_PROTOCOL:
			System.out.println("[Server] Datagram protocol error");
			break;
			
		case WoD_Server_RT_Event.GAME_ACTION:
			
			break;			
			
		case WoD_Server_RT_Event.GAME_NTP:
			System.out.println("[Server] SNTP received at " + new Date().getTime() + " -> " + event.getMessage());
			
			DatagramPacket datagramPacket = (DatagramPacket) event.getData();
			
			String message_parts[] = event.getMessage().split(" ");
			final long server_sendTime = Long.valueOf(message_parts[1]);
			final long client_received_time = Long.valueOf(message_parts[2]);
			final long client_sent_time = Long.valueOf(message_parts[3]);
			final long received_time = new Date().getTime();
			
			
			String clientDirecction[] = String.valueOf(datagramPacket.getSocketAddress()).split(":");
			int udp_port = Integer.valueOf(clientDirecction[clientDirecction.length - 1]);
			
			WoD_Server_Client client = null;
			try {
				client = clientList.get_Client_By_UDP_Port(
						datagramPacket.getAddress().getHostAddress(), 
						udp_port
						);
				int rtt = Long.valueOf((received_time - server_sendTime) - (client_sent_time - client_received_time)).intValue();
				int delay = rtt / 2;
				int offSet = Long.valueOf(client_received_time - server_sendTime).intValue() - delay;
				
				client.setDelay(delay);
				client.setOffset(offSet);
				
				System.out.println("Client " + client.getCliend_Id() + " delay: " + delay + ", offset: " + offSet);
				
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				break;
			}
			
			
			
			
			break;				
			
		default:
			break;
		}
	}
	
	
	public void start_Server() throws Exception
	{
		/**
		 * Proxy section
		 */
		if (isProxyEnabled())
		{
			Socket socket = new Socket(proxy_IP, proxy_port);
			Proxy_client = new WoD_Server_Client(socket);
			Proxy_client.getWoDServerControlChannel().remove_WoD_Server_ControlChannel_Listener(this);
			Proxy_client.getWoDServerControlChannel().add_WoD_Server_ControlChannel_Listener(new WoD_Server_ControlChannel_Listener() {
				
				@Override
				public void notify_Event_WoD_Client_ControlChannel(
						WoD_Server_ControlChannel_Event event) {
					String message_received = (String) event.getData();
					String message_parts[] = message_received.split(" ");
					String message_toSend = null;
					switch (event.getEvent_Type()) {


					
						case WoD_Server_ControlChannel_Event.JOIN_GAME:
							message_received = (String) event.getData();
			
							
							int position = clientList.get_positionOfClient_By_UserName(message_parts[1]);
							if (position != -1)
							{
								/*
								 * If position is different than -1 the username already exists
								 */
								message_toSend = WoD_Net_Protocol_ASCII.GameMenu.Join_Game_KO + " "  + "User already exists.";
								try {
									event.getWoDServerClient().getWoDServerControlChannel().sendData(message_toSend);
								} catch (Exception e) {
									// TODO Auto-generated catch block
									e.printStackTrace();
								}
							}
							else
							{
								/*
								 * Else set backup port and udp port.
								 * Also send "Join_Game_OK" message
								 */
								event.getWoDServerClient().setTcp_backup(Integer.valueOf(message_parts[2]));
								event.getWoDServerClient().setUdp_port(Integer.valueOf(message_parts[3]));
								
								
								message_toSend = WoD_Net_Protocol_ASCII.GameMenu.Join_Game_OK + " ";
								message_toSend += event.getWoDServerClient().getCliend_Id() + " ";
								message_toSend += port_UDP_RT;
								
								
								try {
									event.getWoDServerClient().getWoDServerControlChannel().sendData(message_toSend);
								} catch (Exception e) {
									// TODO Auto-generated catch block
									e.printStackTrace();
								}
							}
							break;
						default:
							break;
						}
					

				}
			});
			
			return;
		}
		
		
		/*
		 * Start TCP Socket Server
		 */
		woD_TCP_Socket_Server = new WoD_TCP_Socket_Server(port_TCP_CONTROL);
		woD_TCP_Socket_Server.add_WoD_TCP_Socket_Server_Listener(this);
		woD_TCP_Socket_Server.start_Server();
		Thread thread_tcp_SocketServer = new Thread(woD_TCP_Socket_Server);
		thread_tcp_SocketServer.start();
		
		/*
		 * Start RT Server
		 */
		woDServerRT = new WoD_Server_RT(port_UDP_RT, 500, 10000);
		woDServerRT.add_WoD_Server_RT_Listener(this);
		woDServerRT.startServer();
		
	}
	
	
	public void send_NTP_Messages()
	{
		for (WoD_Server_Client client : clientList.getClients()) {
			try {
				send_NTP_To_Client(client);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	private void send_NTP_To_Client(WoD_Server_Client client) throws Exception
	{
		String message_ToSend = WoD_Net_Protocol_ASCII.RealTime.HEADER_SNTP + " ";
			message_ToSend += new Date().getTime();
			
		woDServerRT.send_Message_To_Client(client, message_ToSend);
	}
	
}
